<script setup>
import {WebGLRenderer, PerspectiveCamera, Scene,
  PointLight, PlaneGeometry,
  Color, Vector3, BufferAttribute,
  ShaderMaterial,BufferGeometry,
  MathUtils,Mesh,
  DoubleSide
} from 'three'
import * as THREE from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";




/////////////////////////////
// buffer animation system
/////////////////////////////
/////////////////////////////
// Buffer Animation System (Class-based)
/////////////////////////////

class BAS {
  static ShaderChunk = {
    animation_time: `float tDelay = aAnimation.x;
      float tDuration = aAnimation.y;
      float tTime = clamp(uTime - tDelay, 0.0, tDuration);
      float tProgress = ease(tTime, 0.0, 1.0, tDuration);`,

    cubic_bezier: `vec3 cubicBezier(vec3 p0, vec3 c0, vec3 c1, vec3 p1, float t) {
        vec3 tp;
        float tn = 1.0 - t;
        tp.xyz = tn*tn*tn*p0.xyz + 3.0*tn*tn*t*c0.xyz + 3.0*tn*t*t*c1.xyz + t*t*t*p1.xyz;
        return tp;
      }`,

    ease_in_cubic: `float ease(float t, float b, float c, float d) {
        return c*(t/=d)*t*t + b;
      }`,

    ease_in_quad: `float ease(float t, float b, float c, float d) {
        return c*(t/=d)*t + b;
      }`,

    ease_out_cubic: `float ease(float t, float b, float c, float d) {
        return c*((t=t/d - 1.0)*t*t + 1.0) + b;
      }`,

    quaternion_rotation: `vec3 rotateVector(vec4 q, vec3 v) {
        return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
      }
      vec4 quatFromAxisAngle(vec3 axis, float angle) {
        float halfAngle = angle * 0.5;
        return vec4(axis.xyz * sin(halfAngle), cos(halfAngle));
      }`
  };
}

class BASPrefabBufferGeometry extends THREE.BufferGeometry {
  constructor(prefab, count) {
    super();
    this.prefabGeometry = prefab;
    this.prefabCount = count;
    this.prefabVertexCount = prefab.attributes.position.count;
    this.bufferDefaults();
  }

  bufferDefaults() {
    const positionAttr = this.prefabGeometry.attributes.position;
    const srcPositions = positionAttr.array;
    const srcIndex = this.prefabGeometry.index.array;

    const prefabVertexCount = this.prefabVertexCount;
    const srcIndexCount = srcIndex.length;
    const prefabIndices = Array.from(srcIndex);

    const indexBuffer = new Uint32Array(this.prefabCount * srcIndexCount);
    const positionBuffer = new Float32Array(this.prefabCount * prefabVertexCount * 3);

    this.setIndex(new THREE.BufferAttribute(indexBuffer, 1));
    this.setAttribute('position', new THREE.BufferAttribute(positionBuffer, 3));

    // 复制位置数据
    let posOffset = 0;
    for (let i = 0; i < this.prefabCount; i++) {
      positionBuffer.set(srcPositions, posOffset);
      posOffset += srcPositions.length;
    }

    // 复制索引数据
    for (let i = 0; i < this.prefabCount; i++) {
      const vertexOffset = i * prefabVertexCount;
      for (let k = 0; k < srcIndexCount; k++) {
        indexBuffer[i * srcIndexCount + k] = prefabIndices[k] + vertexOffset;
      }
    }
  }

  createAttribute(name, itemSize) {
    const buffer = new Float32Array(this.prefabCount * this.prefabVertexCount * itemSize);
    const attribute = new THREE.BufferAttribute(buffer, itemSize);
    this.setAttribute(name, attribute);
    return attribute;
  }

  computeVertexNormals() {
    const index = this.index;
    const attributes = this.attributes;
    const positions = attributes.position.array;

    if (!attributes.normal) {
      this.setAttribute('normal', new THREE.BufferAttribute(
          new Float32Array(positions.length), 3
      ));
    }

    const normals = attributes.normal.array;
    const indices = index.array;
    // const prefabIndexCount = this.prefabGeometry.faces.length * 3;

    // 获取索引属性（替代 faces.length）
    const prefabIndexAttr = this.prefabGeometry.index;
    const prefabIndices = prefabIndexAttr.array;
    const prefabIndexCount = prefabIndices.length;

    const pA = new Vector3();
    const pB = new Vector3();
    const pC = new Vector3();
    const cb = new Vector3();
    const ab = new Vector3();

    for (let i = 0; i < prefabIndexCount; i += 3) {
      const vA = indices[i] * 3;
      const vB = indices[i + 1] * 3;
      const vC = indices[i + 2] * 3;

      pA.fromArray(positions, vA);
      pB.fromArray(positions, vB);
      pC.fromArray(positions, vC);

      cb.subVectors(pC, pB);
      ab.subVectors(pA, pB);
      cb.cross(ab);

      normals[vA] += cb.x;
      normals[vA + 1] += cb.y;
      normals[vA + 2] += cb.z;

      normals[vB] += cb.x;
      normals[vB + 1] += cb.y;
      normals[vB + 2] += cb.z;

      normals[vC] += cb.x;
      normals[vC + 1] += cb.y;
      normals[vC + 2] += cb.z;
    }

    // 修改法线复制逻辑（基于顶点数而非面数）
    for (let j = 1; j < this.prefabCount; j++) {
      const instanceOffset = j * this.prefabVertexCount;
      for (let k = 0; k < this.prefabVertexCount; k++) {
        const dstIndex = (instanceOffset + k) * 3;
        const srcIndex = k * 3;
        normals[dstIndex] = normals[srcIndex];
        normals[dstIndex + 1] = normals[srcIndex + 1];
        normals[dstIndex + 2] = normals[srcIndex + 2];
      }
    }

    this.normalizeNormals();
    attributes.normal.needsUpdate = true;
  }
}

class BASBaseAnimationMaterial extends THREE.ShaderMaterial {
  constructor(parameters) {
    super(parameters);
    this.shaderFunctions = [];
    this.shaderParameters = [];
    this.shaderVertexInit = [];
    this.shaderTransformNormal = [];
    this.shaderTransformPosition = [];
  }

  _concatFunctions() {
    return this.shaderFunctions.join('\n');
  }

  _concatParameters() {
    return this.shaderParameters.join('\n');
  }

  _concatVertexInit() {
    return this.shaderVertexInit.join('\n');
  }

  _concatTransformNormal() {
    return this.shaderTransformNormal.join('\n');
  }

  _concatTransformPosition() {
    return this.shaderTransformPosition.join('\n');
  }

  setUniformValues(values) {
    for (const key in values) {
      if (this.uniforms[key]) {
        const uniform = this.uniforms[key];
        const value = values[key];

        switch (uniform.type) {
          case 'c':
            uniform.value.set(value);
            break;
          case 'v2':
          case 'v3':
          case 'v4':
            uniform.value.copy(value);
            break;
          case 'f':
          case 't':
            uniform.value = value;
        }
      }
    }
  }
}

class BASPhongAnimationMaterial extends BASBaseAnimationMaterial {
  constructor(parameters, uniformValues) {
    super(parameters);
    const phongShader = THREE.ShaderLib.phong;

    this.uniforms = THREE.UniformsUtils.merge([
      phongShader.uniforms,
      this.uniforms
    ]);

    this.lights = true;
    this.vertexShader = this._concatVertexShader();
    this.fragmentShader = phongShader.fragmentShader;

    if (uniformValues.map) this.defines.USE_MAP = '';
    if (uniformValues.normalMap) this.defines.USE_NORMALMAP = '';

    this.setUniformValues(uniformValues);
  }

  _concatVertexShader() {
    return `
      #define PHONG
      varying vec3 vViewPosition;

      #ifndef FLAT_SHADED
        varying vec3 vNormal;
      #endif

      ${THREE.ShaderChunk.common}
      ${THREE.ShaderChunk.uv_pars_vertex}
      ${THREE.ShaderChunk.uv2_pars_vertex}
      ${THREE.ShaderChunk.displacementmap_pars_vertex}
      ${THREE.ShaderChunk.envmap_pars_vertex}
      ${THREE.ShaderChunk.lights_phong_pars_vertex}
      ${THREE.ShaderChunk.color_pars_vertex}
      ${THREE.ShaderChunk.morphtarget_pars_vertex}
      ${THREE.ShaderChunk.skinning_pars_vertex}
      ${THREE.ShaderChunk.shadowmap_pars_vertex}
      ${THREE.ShaderChunk.logdepthbuf_pars_vertex}

      ${this._concatFunctions()}
      ${this._concatParameters()}

      void main() {
        ${this._concatVertexInit()}
        ${THREE.ShaderChunk.uv_vertex}
        ${THREE.ShaderChunk.uv2_vertex}
        ${THREE.ShaderChunk.color_vertex}
        ${THREE.ShaderChunk.beginnormal_vertex}

        ${this._concatTransformNormal()}

        ${THREE.ShaderChunk.morphnormal_vertex}
        ${THREE.ShaderChunk.skinbase_vertex}
        ${THREE.ShaderChunk.skinnormal_vertex}
        ${THREE.ShaderChunk.defaultnormal_vertex}

        #ifndef FLAT_SHADED
          vNormal = normalize(transformedNormal);
        #endif

        ${THREE.ShaderChunk.begin_vertex}
        ${this._concatTransformPosition()}

        ${THREE.ShaderChunk.displacementmap_vertex}
        ${THREE.ShaderChunk.morphtarget_vertex}
        ${THREE.ShaderChunk.skinning_vertex}
        ${THREE.ShaderChunk.project_vertex}
        ${THREE.ShaderChunk.logdepthbuf_vertex}

        vViewPosition = -mvPosition.xyz;

        ${THREE.ShaderChunk.worldpos_vertex}
        ${THREE.ShaderChunk.envmap_vertex}
        ${THREE.ShaderChunk.lights_phong_vertex}
        ${THREE.ShaderChunk.shadowmap_vertex}
      }
    `;
  }
}
// import {PhongAnimationMaterial} from "three-bas"
var mContainer;
var mCamera, mRenderer;
var mControls;

var mScene;

var mParticleCount = 100000; // <-- change this number!
var mParticleSystem;

var mTime = 0.0;
var mTimeStep = (1/60);
var mDuration = 20;

const container = ref()

function init() {
  initTHREE();
  initControls();
  initParticleSystem();
  // const obj = new Object();
  requestAnimationFrame(tick);
  window.addEventListener('resize', resize, false);
}

function initTHREE() {
  mRenderer = new WebGLRenderer({antialias: true});
  mRenderer.setSize(window.innerWidth, window.innerHeight);

  // mContainer = document.getElementById('three-container');
  container.value.appendChild(mRenderer.domElement);

  mCamera = new PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 5000);
  mCamera.position.set(0, 600, 600);

  mScene = new Scene();

  var light;

  light = new PointLight(0xffffff, 4, 1000, 2);
  light.position.set(0, 400, 0);
  mScene.add(light);
}

function initControls() {
  mControls = new OrbitControls(mCamera, mRenderer.domElement);
}

function initParticleSystem() {
  var prefabGeometry = new PlaneGeometry(4, 4);
  var bufferGeometry = new BASPrefabBufferGeometry(prefabGeometry, mParticleCount);

  bufferGeometry.computeVertexNormals();

  // generate additional geometry data
  var aOffset = bufferGeometry.createAttribute('aOffset', 1);
  var aStartPosition = bufferGeometry.createAttribute('aStartPosition', 3);
  var aControlPoint1 = bufferGeometry.createAttribute('aControlPoint1', 3);
  var aControlPoint2 = bufferGeometry.createAttribute('aControlPoint2', 3);
  var aEndPosition = bufferGeometry.createAttribute('aEndPosition', 3);
  var aAxisAngle = bufferGeometry.createAttribute('aAxisAngle', 4);
  var aColor = bufferGeometry.createAttribute('color', 3);

  var i, j, offset;

  // buffer time offset
  var delay;

  for (i = 0, offset = 0; i < mParticleCount; i++) {
    delay = i / mParticleCount * mDuration;
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aOffset.array[offset++] = delay;
    }
  }
  // buffer start positions
  var x, y, z;
  for (i = 0, offset = 0; i < mParticleCount; i++) {
    x = -1000;
    y = 0;
    z = 0;
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aStartPosition.array[offset++] = x;
      aStartPosition.array[offset++] = y;
      aStartPosition.array[offset++] = z;
    }
  }

  // buffer control points

  for (i = 0, offset = 0; i < mParticleCount; i++) {
    x = MathUtils.randFloat(-400, 400);
    y = MathUtils.randFloat(400, 600);
    z = MathUtils.randFloat(-1200, -800);
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aControlPoint1.array[offset++] = x;
      aControlPoint1.array[offset++] = y;
      aControlPoint1.array[offset++] = z;
    }
  }

  for (i = 0, offset = 0; i < mParticleCount; i++) {
    x = MathUtils.randFloat(-400, 400);
    y = MathUtils.randFloat(-600, -400);
    z = MathUtils.randFloat(800, 1200);
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aControlPoint2.array[offset++] = x;
      aControlPoint2.array[offset++] = y;
      aControlPoint2.array[offset++] = z;
    }
  }

  // buffer end positions

  for (i = 0, offset = 0; i < mParticleCount; i++) {
    x = 1000;
    y = 0;
    z = 0;
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aEndPosition.array[offset++] = x;
      aEndPosition.array[offset++] = y;
      aEndPosition.array[offset++] = z;
    }
  }

  // buffer axis angle
  var axis = new Vector3();
  var angle = 0;
  for (i = 0, offset = 0; i < mParticleCount; i++) {
    axis.x = MathUtils.randFloatSpread(2);
    axis.y = MathUtils.randFloatSpread(2);
    axis.z = MathUtils.randFloatSpread(2);
    axis.normalize();
    angle = Math.PI * MathUtils.randInt(16, 32);
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aAxisAngle.array[offset++] = axis.x;
      aAxisAngle.array[offset++] = axis.y;
      aAxisAngle.array[offset++] = axis.z;
      aAxisAngle.array[offset++] = angle;
    }
  }
  // buffer color
  var color = new Color();
  var h, s, l;
  for (i = 0, offset = 0; i < mParticleCount; i++) {
    h = i / mParticleCount;
    s = MathUtils.randFloat(0.4, 0.6);
    l = MathUtils.randFloat(0.4, 0.6);
    color.setHSL(h, s, l);
    for (j = 0; j < bufferGeometry.prefabVertexCount; j++) {
      aColor.array[offset++] = color.r;
      aColor.array[offset++] = color.g;
      aColor.array[offset++] = color.b;
    }
  }


  var material = new BASPhongAnimationMaterial(
      {
        vertexColors: true,
        flatShading: true,
        side: DoubleSide,  // 修复1：添加THREE前缀
        uniforms: {
          uTime: { type: 'f', value: 0 },
          uDuration: { type: 'f', value: mDuration }
        },
        shaderFunctions: [
          BAS.ShaderChunk['quaternion_rotation'],
          BAS.ShaderChunk['cubic_bezier'],
          // 修复2：添加缺失的ease函数
          `float ease(float t, float b, float c, float d) {
        return c * ((t = t / d - 1.0) * t * t + 1.0) + b;
      }`
        ],
        shaderParameters: [
          'uniform float uTime;',
          'uniform float uDuration;',
          'attribute float aOffset;',         // 修复3：WebGL 1.0用attribute
          'attribute vec3 aStartPosition;',   // 非in关键字
          'attribute vec3 aControlPoint1;',
          'attribute vec3 aControlPoint2;',
          'attribute vec3 aEndPosition;',
          'attribute vec4 aAxisAngle;'
        ],
        shaderVertexInit: [
          // 修复4：使用安全钳制计算进度
          'float tTime = mod(uTime + aOffset, uDuration);',
          'float tProgress = clamp(tTime / uDuration, 0.0, 1.0);',
          'float angle = aAxisAngle.w * tProgress;',
          'vec4 tQuat = quatFromAxisAngle(aAxisAngle.xyz, angle);'
        ],
        shaderTransformNormal: [
          'objectNormal = rotateVector(tQuat, objectNormal);'
        ],
        shaderTransformPosition: [
          'transformed = rotateVector(tQuat, transformed);',
          // 修复5：添加贝塞尔曲线位置偏移
          'transformed += cubicBezier(aStartPosition, aControlPoint1, aControlPoint2, aEndPosition, tProgress);'
        ]
      },
      // Phong材质参数优化（降低高光强度）
      {
        specular: 0x333333,  // 原0xff0000改为深灰[2](@ref)
        shininess: 60        // 提高光泽度使高光更集中[6](@ref)
      }
  );

  mParticleSystem = new Mesh(bufferGeometry, material);
  // because the bounding box of the particle system does not reflect its on-screen size
  // set this to false to prevent the whole thing from disappearing on certain angles
  mParticleSystem.frustumCulled = false;

  mScene.add(mParticleSystem);
}

function tick() {
  update();
  render();

  mTime += mTimeStep;
  mTime %= mDuration;

  requestAnimationFrame(tick);
}

function update() {
  mControls.update();

  mParticleSystem.material.uniforms['uTime'].value = mTime;
}

function render() {
  mRenderer.render(mScene, mCamera);
}

function resize() {
  mCamera.aspect = window.innerWidth / window.innerHeight;
  mCamera.updateProjectionMatrix();

  mRenderer.setSize(window.innerWidth, window.innerHeight);
}

onMounted(() => {
  nextTick(() => init())
})
</script>

<template>
<div class="container" ref="container"></div>
</template>

<style scoped lang="scss">
.container {
  width: 1200px;
  height: 600px;
}
</style>